feat(init): detect agent/CI environments and skip interactive prompts#1264
feat(init): detect agent/CI environments and skip interactive prompts#1264
Conversation
Uses `std-env` v4's `isAgent`, `isCI`, and `hasTTY` to automatically detect when the CLI is run non-interactively (AI agents, CI pipelines, piped input) and apply sensible defaults instead of hanging on prompts. Adds two explicit flags for opt-in: - `--defaults` / `-y`: accept all defaults (like `npm init -y`) - `--no-interactive`: same, more explicit for scripting contexts When non-interactive mode is detected, the CLI: - Logs the reason (agent name, CI, no TTY, or flag) - Displays a full options reference — including all available templates fetched live — so agents can discover flags and re-run with custom settings - Auto-selects: template=minimal, dir=template's defaultDir, package manager=detected or npm, gitInit=false, modules=skipped - Fails fast (instead of hanging) when the target directory already exists, instructing the caller to pass `--force` All existing interactive behaviour is preserved when a TTY is present and none of the new flags are set. https://claude.ai/code/session_01LsDZBSg7peDmxh6QW33ag8
- Move detectCurrentPackageManager() before template load so package manager is known upfront - Compute all effective defaults (template, dir, pm, gitInit, install, modules) before any action is taken and display them together in a 'Proceeding with:' section at the bottom of the options note - Show all available templates with the default marked (← default) - Fix module examples to use @nuxt/content,@nuxt/ui,@nuxt/image - Remove scattered 'Auto-selected X' log lines — they are now covered by the single consolidated note - Simplify the non-interactive module-skip branch https://claude.ai/code/session_01LsDZBSg7peDmxh6QW33ag8
When running non-interactively (agent/CI/no-TTY) without a project directory, show the available options note then exit cleanly instead of proceeding with defaults. The agent reads the output, chooses the right flags, and re-runs with an explicit <dir>. nuxi init → shows options, exits (no project created) nuxi init my-app → creates my-app with defaults nuxi init my-app -t v3 → creates my-app with v3 template https://claude.ai/code/session_01LsDZBSg7peDmxh6QW33ag8
commit: |
📝 WalkthroughWalkthroughAdds Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/nuxi/src/commands/init.ts (1)
174-223: Consider centralizing resolved defaults to avoid drift between “note” and execution paths.Effective values are computed in the note block and then re-derived later for actual execution. A single
resolvedOptionsobject would reduce maintenance risk.Also applies to: 236-240, 271-276, 443-448, 465-469
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/nuxi/src/commands/init.ts` around lines 174 - 223, Create a single resolvedOptions object (e.g. { template, dir, packageManager, gitInit, install, modules, offline, force }) computed once in init.ts and replace the duplicated local computations (the variables effectiveTemplate, effectiveDir, effectivePM, effectiveGitInit, effectiveInstall, effectiveModules) so both the non-interactive "note" block and the later execution paths reuse the same resolvedOptions; update all referenced sites (the note block and the other duplicated ranges you flagged around lines 236-240, 271-276, 443-448, 465-469) to read from resolvedOptions to avoid drift and ensure consistent defaults across the codepaths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/nuxi/src/commands/init.ts`:
- Around line 130-135: The multiline boolean assignment for const
isNonInteractive (and the other multiline boolean expression further down) has
inconsistent indentation that breaks ESLint; reformat these expressions so
continuation lines are consistently indented — either align each boolean operand
under the first operand or wrap the RHS in parentheses and indent each
subsequent line one level (e.g., two spaces or one tab) so the operators (||)
and the operands line up consistently; update the lines that contain
isNonInteractive and the similar boolean block near the later expression to
match the project's multiline indentation style.
---
Nitpick comments:
In `@packages/nuxi/src/commands/init.ts`:
- Around line 174-223: Create a single resolvedOptions object (e.g. { template,
dir, packageManager, gitInit, install, modules, offline, force }) computed once
in init.ts and replace the duplicated local computations (the variables
effectiveTemplate, effectiveDir, effectivePM, effectiveGitInit,
effectiveInstall, effectiveModules) so both the non-interactive "note" block and
the later execution paths reuse the same resolvedOptions; update all referenced
sites (the note block and the other duplicated ranges you flagged around lines
236-240, 271-276, 443-448, 465-469) to read from resolvedOptions to avoid drift
and ensure consistent defaults across the codepaths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: dfda46aa-f2f3-4a95-b0f4-36571b8806e5
📒 Files selected for processing (1)
packages/nuxi/src/commands/init.ts
…ndition The original condition (!template || !dir) already covers every case where templates need to be loaded for prompts or the options note. Adding isNonInteractive caused an unnecessary network fetch when an agent provided both --template and dir (only to populate a template list in the note that wasn't needed). The 'Proceeding with:' section in the note still works correctly without it. https://claude.ai/code/session_01LsDZBSg7peDmxh6QW33ag8
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/nuxi/src/commands/init.ts (1)
188-189: Consider extracting the string 'false' coercion pattern.The
(ctx.args.gitInit as unknown) === 'false'pattern appears multiple times (here, line 465, line 487). A small helper liketoBool(value)could reduce duplication and clarify intent.♻️ Optional: Extract helper function
// Add near the top of the file or in a shared utils function parseBoolArg(value: unknown): boolean | undefined { if (value === 'false') return false if (value === 'true') return true return value as boolean | undefined }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/nuxi/src/commands/init.ts` around lines 188 - 189, The code duplicates a string-'false' coercion pattern when computing effectiveGitInit and effectiveInstall; extract a small helper (e.g., parseBoolArg or toBool) that accepts unknown and returns boolean | undefined by mapping 'false'->false, 'true'->true and otherwise returning the value as boolean | undefined, then replace the inline checks in effectiveGitInit and effectiveInstall (and any other occurrences like the ones noted around lines using ctx.args.gitInit / ctx.args.install) to call that helper to reduce duplication and clarify intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/nuxi/src/commands/init.ts`:
- Around line 274-276: The if-branch that sets dir = defaultDir when
isNonInteractive is unreachable and should be removed; locate the conditional
using isNonInteractive and dir (the block "if (isNonInteractive) { dir =
defaultDir }") in the init command and delete that branch so the code flow
relies on the existing early-exit behavior around ctx.args.dir, or if you intend
non-interactive to auto-select a directory instead, adjust the earlier
early-exit logic that checks ctx.args.dir rather than keeping this unreachable
fallback.
---
Nitpick comments:
In `@packages/nuxi/src/commands/init.ts`:
- Around line 188-189: The code duplicates a string-'false' coercion pattern
when computing effectiveGitInit and effectiveInstall; extract a small helper
(e.g., parseBoolArg or toBool) that accepts unknown and returns boolean |
undefined by mapping 'false'->false, 'true'->true and otherwise returning the
value as boolean | undefined, then replace the inline checks in effectiveGitInit
and effectiveInstall (and any other occurrences like the ones noted around lines
using ctx.args.gitInit / ctx.args.install) to call that helper to reduce
duplication and clarify intent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3ee15017-9503-4ce1-a8d1-f4ac2d4094fc
📒 Files selected for processing (1)
packages/nuxi/src/commands/init.ts
📦 Bundle Size Comparison📈 nuxi
📈 nuxt-cli
📈 create-nuxt
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/nuxi/src/commands/init.ts (1)
188-189: Minor style inconsistency in type casting.Lines 188 and 460 handle the string
'false'from citty differently:
- Line 188:
(ctx.args.gitInit as unknown) === 'false'- Line 460:
ctx.args.gitInit === 'false' as unknownBoth work, but the inconsistency could cause confusion. Consider unifying the pattern.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/nuxi/src/commands/init.ts` around lines 188 - 189, The code inconsistently casts ctx.args.gitInit to check for the string 'false' (used in effectiveGitInit) versus casting the comparison in another place; standardize the pattern by using the same explicit cast/location for the comparison across usages (e.g., always cast ctx.args.gitInit to unknown or string before comparing), update the check in the effectiveGitInit assignment to match the other occurrence so both use the same style, and ensure any related checks (like effectiveInstall) follow the same casting convention; specifically look for and update references to effectiveGitInit and ctx.args.gitInit to use the unified pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/nuxi/src/commands/init.ts`:
- Around line 188-189: The code inconsistently casts ctx.args.gitInit to check
for the string 'false' (used in effectiveGitInit) versus casting the comparison
in another place; standardize the pattern by using the same explicit
cast/location for the comparison across usages (e.g., always cast
ctx.args.gitInit to unknown or string before comparing), update the check in the
effectiveGitInit assignment to match the other occurrence so both use the same
style, and ensure any related checks (like effectiveInstall) follow the same
casting convention; specifically look for and update references to
effectiveGitInit and ctx.args.gitInit to use the unified pattern.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4c6de8bf-3229-4e24-9d31-ef760881db95
📒 Files selected for processing (1)
packages/nuxi/src/commands/init.ts
|
Good to be reviewed @danielroe |
posva
left a comment
There was a problem hiding this comment.
This is what I think. Some agents automatically run --help to auto discover what they can do or they use it as soon as the command fails. So I really think you can keep this very lean in terms of changes as long as the --help text is good. I would say the only missing part are templates which can be listed if you detect an agent
| alias: 'y', | ||
| description: 'Use defaults for all prompts (useful for CI/agent environments)', | ||
| }, | ||
| interactive: { |
There was a problem hiding this comment.
Currently interactive and defaults seem to only affect the same part of the code. I think interactive should not exist, it also makes it weird to have a default value of true (do you pass --no-interactive? --interactive=false?). I find the -y to be better
| type: 'string', | ||
| description: 'Use Nuxt nightly release channel (3x or latest)', | ||
| }, | ||
| defaults: { |
There was a problem hiding this comment.
Should probably be named yes, it's more conventional (cf npm init -h)
| // it needs to re-run with explicit flags. Exit without creating anything. | ||
| if (!ctx.args.dir) { | ||
| outro('Re-run with a project directory to proceed, e.g: nuxi init <dir>') | ||
| process.exit(0) |
There was a problem hiding this comment.
should be non-zero since the run didn't succeed, probably 2 because it's missing parts of the command but I don't think there is a standard on the actual value (other than being non-zero)
| ` ${colors.cyan(name.padEnd(18))} ${data?.description ?? ''}${name === DEFAULT_TEMPLATE_NAME ? colors.dim(' ← default') : ''}`, | ||
| ) | ||
|
|
||
| note( |
There was a problem hiding this comment.
If you want to simplify this, you could just display the help, after that display the template list (I think that's the only value that requires a specific value that isn't already mentioned in the help)
| const isNonInteractive | ||
| = ctx.args.defaults === true | ||
| || ctx.args.interactive === false | ||
| || isAgent // AI coding agent (Claude, Cursor, Copilot, Devin, Gemini…) |
There was a problem hiding this comment.
this will also be positive if a user is using a shell from within a coding harness.
I think it's better to gate this on whether the TTY is interactive or not, rather than whether an agent has been detected
There was a problem hiding this comment.
I agree with this: hasTTY should be enough. Displaying the help for the setup to be run in one go is, IMO, the best option. It's worth noting that it will also render #1287 ineffective, but I believe that's the correct path for this kind of CLI because if by any chance we want later to support agent-interactive commands, e.g. nuxi module add, we can probably opt out const isNonInteractive = !ctx.args.command && !hasTTY
| note( | ||
| [ | ||
| colors.bold('Re-run with any of these flags to customise:'), | ||
| '', | ||
| `${colors.cyan('--template')} <name>`, | ||
| ...templateLines, | ||
| '', | ||
| `${colors.cyan('<dir>')} Project directory`, | ||
| `${colors.cyan('--packageManager')} <pm> npm | pnpm | yarn | bun | deno`, | ||
| `${colors.cyan('--gitInit')} / ${colors.cyan('--no-gitInit')} Initialise git repo`, | ||
| `${colors.cyan('--install')} / ${colors.cyan('--no-install')} Install dependencies`, | ||
| `${colors.cyan('--modules')} <m1,m2,...> e.g. ${colors.dim('@nuxt/content,@nuxt/ui,@nuxt/image')}`, | ||
| `${colors.dim(' Full list: https://nuxt.com/modules')}`, | ||
| `${colors.cyan('--no-modules')} Skip module prompt`, | ||
| `${colors.cyan('--force')} Override existing directory`, | ||
| `${colors.cyan('--offline')} Use cached templates`, | ||
| '', | ||
| colors.bold('Proceeding with:'), | ||
| ` template: ${colors.cyan(effectiveTemplate)}`, | ||
| ` directory: ${colors.cyan(effectiveDir)}`, | ||
| ` packageManager: ${colors.cyan(effectivePM)}`, | ||
| ` gitInit: ${colors.cyan(String(effectiveGitInit))}`, | ||
| ` install: ${colors.cyan(String(effectiveInstall))}`, | ||
| ` modules: ${colors.cyan(effectiveModules)}`, | ||
| ].join('\n'), | ||
| 'Available options', | ||
| ) |
There was a problem hiding this comment.
this will quickly become out of date - can we instead harness citty's own tools?
additionally it's hard-coded to be a specific width (padEnd(18)), which is not really needed for agents and is also very dependent on the content of the
Uses
std-envv4'sisAgent,isCI, andhasTTYto automaticallydetect when the CLI is run non-interactively (AI agents, CI pipelines,
piped input) and apply sensible defaults instead of hanging on prompts.
Adds two explicit flags for opt-in:
--defaults/-y: accept all defaults (likenpm init -y)--no-interactive: same, more explicit for scripting contextsWhen non-interactive mode is detected, the CLI:
fetched live — so agents can discover flags and re-run with custom
settings
manager=detected or npm, gitInit=false, modules=skipped
exists, instructing the caller to pass
--forceAll existing interactive behaviour is preserved when a TTY is present
and none of the new flags are set.